package com.fredia.terp.util.encrypt;

import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import javax.crypto.Cipher;

/**
 * RSA加密工具类 非对称加密
 * 
 * @author : wangyong
 * @since : 2017年6月27日
 * @version : v0.0.1
 */
public class RsaEncryptionUtils {

	/* 加密方式 */
	private static final String ENCRYPTION_INSTANCE = "RSA";

	/* 字符集编码 */
	private static final String CHARSETNAME = "UTF-8";

	/* 生成key长度 */
	private static final Integer KEYSIZE = 1024;

	/* 解密字节长度 */
	private static final Integer MAX_DECRYPT_BLOCK = KEYSIZE / 8;

	/* 加密字节长度 */
	private static final Integer MAX_ENCRYPT_BLOCK = KEYSIZE / 8 - 11;

	public static void main(String[] args) throws Exception {
		String encryption = args[0];
		initializeSecretKey(encryption);
	}

	/**
	 * 初始化秘钥
	 * 
	 * @param encryption
	 * @throws Exception
	 * @author : wangyong
	 * @since : 2017年6月27日
	 */
	public static void initializeSecretKey(String encryption) throws Exception {
		KeyPairGenerator keyPairGenerator;
		if (encryption != null && !"".equals(encryption)) {
			keyPairGenerator = KeyPairGenerator.getInstance(ENCRYPTION_INSTANCE);
		} else {
			keyPairGenerator = KeyPairGenerator.getInstance(encryption);
		}

		keyPairGenerator.initialize(KEYSIZE);
		KeyPair keyPair = keyPairGenerator.generateKeyPair();
		RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();// 公钥
		RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();// 私钥
		System.out.println("=====>publicKey : " + Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded()));
		System.out.println("=============================================================================");
		System.out.println("=====>privateKey : " + Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded()));
	}

	/**
	 * 私钥加密
	 * 
	 * @param priKey
	 * @param encryptStr
	 * @return
	 * @throws Exception
	 * @author : wangyong
	 * @since : 2017年6月27日
	 */
	public static String privateKeyEncryption(String priKey, String encryptStr) throws Exception {
		String encryptContent = null;
		if ((priKey == null || priKey.equals("")) || (encryptStr == null || encryptStr.equals(""))) {
			throw new RuntimeException("privateKeyEncryption priKey or encryptStr is null...");
		}

		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(priKey));
		KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTION_INSTANCE);
		PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);

		byte[] encryptedData = blockEncryption(encryptStr, cipher);
		if (encryptedData != null) {
			encryptContent = Base64.getEncoder().encodeToString(encryptedData);
		}
		return encryptContent;
	}

	/**
	 * 公钥加密
	 * 
	 * @param pubKey
	 * @param encryptStr
	 * @return
	 * @throws Exception
	 * @author : wangyong
	 * @since : 2017年6月27日
	 */
	public static String publicKeyEncryption(String pubKey, String encryptStr) throws Exception {
		String encryptContent = null;
		if ((pubKey == null || pubKey.equals("")) || (encryptStr == null || encryptStr.equals(""))) {
			throw new RuntimeException("publicKeyEncryption pubKey or encryptStr is null...");
		}

		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));
		KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTION_INSTANCE);
		PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);

		byte[] encryptedData = blockEncryption(encryptStr, cipher);
		if (encryptedData != null) {
			encryptContent = Base64.getEncoder().encodeToString(encryptedData);
		}
		return encryptContent;
	}

	/**
	 * 公钥解密
	 * 
	 * @param pubKey
	 * @param decryptionStr
	 * @return
	 * @throws Exception
	 * @author : wangyong
	 * @since : 2017年6月27日
	 */
	public static String publicKeyDecryption(String pubKey, String decryptionStr) throws Exception {
		String decryptionContent = null;
		if ((pubKey == null || pubKey.equals("")) || (decryptionStr == null || decryptionStr.equals(""))) {
			throw new RuntimeException("publicKeyDecryption pubKey or decryptionStr is null...");
		}

		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey));
		KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTION_INSTANCE);
		PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, publicKey);

		byte[] decryptedData = segmentedDecryption(decryptionStr, cipher);
		if (decryptedData != null) {
			decryptionContent = new String(decryptedData, CHARSETNAME);
		}
		return decryptionContent;
	}

	/**
	 * 私钥解密
	 * 
	 * @param priKey
	 * @param decryptionStr
	 * @return
	 * @throws Exception
	 * @author : wangyong
	 * @since : 2017年6月27日
	 */
	public static String privateKeyDecryption(String priKey, String decryptionStr) throws Exception {
		String decryptionContent = null;
		if ((priKey == null || priKey.equals("")) || (decryptionStr == null || decryptionStr.equals(""))) {
			throw new RuntimeException("privateKeyDecryption priKey or decryptionStr is null...");
		}

		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(priKey));
		KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPTION_INSTANCE);
		PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
		cipher.init(Cipher.DECRYPT_MODE, privateKey);

		byte[] decryptedData = segmentedDecryption(decryptionStr, cipher);
		if (decryptedData != null) {
			decryptionContent = new String(decryptedData, CHARSETNAME);
		}
		return decryptionContent;
	}

	/**
	 * 对数据分段加密
	 * 
	 * @param encryptStr
	 * @param cipher
	 * @return
	 * @throws Exception
	 * @author : wangyong
	 * @since : 2017年6月27日
	 */
	private static byte[] blockEncryption(String encryptStr, Cipher cipher) throws Exception {
		byte[] encryptedData = null;
		if (encryptStr == null || encryptStr.equals("")) {
			throw new RuntimeException("blockEncryption encryptStr is null...");
		}

		ByteArrayOutputStream out = null;
		try {
			int inputLen = encryptStr.getBytes(CHARSETNAME).length;
			out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			// 对数据分段加密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
					cache = cipher.doFinal(encryptStr.getBytes(CHARSETNAME), offSet, MAX_ENCRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(encryptStr.getBytes(CHARSETNAME), offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_ENCRYPT_BLOCK;
			}
			encryptedData = out.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e); // 将异常抛出
		} finally {
			if (out != null) {
				out.close();
			}
		}
		return encryptedData;
	}

	/**
	 * 对数据分段解密
	 * 
	 * @param decryptionStr
	 * @param cipher
	 * @return
	 * @throws Exception
	 * @author : wangyong
	 * @since : 2017年6月27日
	 */
	private static byte[] segmentedDecryption(String decryptionStr, Cipher cipher) throws Exception {
		byte[] decryptedData = null;
		if (decryptionStr == null || decryptionStr.equals("")) {
			throw new RuntimeException("segmentedDecryption decryptionStr is null...");
		}

		ByteArrayOutputStream out = null;
		try {
			int inputLen = Base64.getDecoder().decode(decryptionStr).length;
			out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			// 对数据分段解密
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
					cache = cipher.doFinal(Base64.getDecoder().decode(decryptionStr), offSet, MAX_DECRYPT_BLOCK);
				} else {
					cache = cipher.doFinal(Base64.getDecoder().decode(decryptionStr), offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * MAX_DECRYPT_BLOCK;
			}
			decryptedData = out.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e); // 将异常抛出
		} finally {
			if (out != null) {
				out.close();
			}
		}
		return decryptedData;
	}

	/**
	 * =============================================== 测试RSA 非对称加密 example
	 * ===============================================
	 */

	/*
	 * private static final String pubKey =
	 * "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCbBHpqcFKceaSjxGTRW/wExRrXFA5AlO4D4WgmF10D9ljpU8eVv1MvR+Muf1g0cNd+JctII9Ti53YNLHdS0OxtIkoX1n5vvaZl6OGoHcEtQV3u4PKg7S1qw2aXZK/jWAIGxoWwMh+0gPHf/rtk0NnpC65xDapu5aNizZ2nofGr9QIDAQAB";
	 * private static final String priKey =
	 * "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJsEempwUpx5pKPEZNFb/ATFGtcUDkCU7gPhaCYXXQP2WOlTx5W/Uy9H4y5/WDRw134ly0gj1OLndg0sd1LQ7G0iShfWfm+9pmXo4agdwS1BXe7g8qDtLWrDZpdkr+NYAgbGhbAyH7SA8d/+u2TQ2ekLrnENqm7lo2LNnaeh8av1AgMBAAECgYBpH3lQkwlCua8vq7Be1Y0rQus3rY8YtqZOFV7GbhOS601SNZFkeJGVv59NWehb76YDEugu7LWihiplNqFYlPuu2ehamUCxLhMkqcmlMvq3up6fIOIoNBNEnEkoCk0rYhiOYnuqZN4uf+Et4qynChhHkUUwHu6C90elrS4jdZwlBQJBAOQKwgXUA8Se/vp1NsudMnn0UWPhn1ZuPExkNaW/h0rGKH/3SsVv0+6mSPez+k8lWOrGj2dkzbegseM69GCr8wMCQQCuBcyvQURd9/RrUelL0+6VK5FiK6QMPwNtZZrcedb77MLGuFks4kVf1/kLLFcqWDBW1v7/ORE197H0Ecn4BbenAkAePp8FDt2BEmVW6nzd/HdZXWH+EUqEuBIfP5st5H0ZGbb9Ydr03KQLHtrGn4QvQxDL8pHKcMAn/pQZQxRuTd/pAkBOh2N0epllZi0v+Mi0ZTC2s/BpQpRGynC7fyxA7eJr0M2cazginwD7XTSPLDUxcJZvuZlcht4TYnE1WImxmLLlAkEAmabUqFWJXW8QY9NoSgeRBTtQwYJxP9YTYSg5pZzcUxj+jckPGMGUVAx+NcQri66Sni4XzoGipn1CH2NojSc97Q==";
	 * 
	 * public static void main(String[] args) throws Exception { // 初始化秘钥 //
	 * initializeSecretKey();
	 * 
	 * String str =
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你 你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你"
	 * + "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你"
	 * + "" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你"
	 * + "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你好你好，"
	 * + "我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你"
	 * + "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" +
	 * "你好你好你好，我在这里，你在哪里。你好你好你好，我在这里，你在哪里。你好你" + "。"; // 私钥加密 String str1 =
	 * privateKeyEncryption(priKey, str); logger.debug(str1); // 公钥解密
	 * 
	 * String str2 = publicKeyDecryption(pubKey, str1); logger.debug(str2);
	 * 
	 * // 公钥加密 String str3 = publicKeyEncryption(pubKey, str);
	 * logger.debug(str3); // 私钥解密 String str4 = privateKeyDecryption(priKey,
	 * str3); logger.debug(str4);
	 * 
	 * }
	 */

}
